

<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
<head>
  <meta charset="utf-8">
  
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="Description" content="scikit-learn: machine learning in Python">

  
  <title>1.2. Linear and Quadratic Discriminant Analysis &mdash; scikit-learn 0.22 documentation</title>
  
  <link rel="canonical" href="http://scikit-learn.org/stable/modules/lda_qda.html" />

  
  <link rel="shortcut icon" href="../_static/favicon.ico"/>
  

  <link rel="stylesheet" href="../_static/css/vendor/bootstrap.min.css" type="text/css" />
  <link rel="stylesheet" href="../_static/gallery.css" type="text/css" />
  <link rel="stylesheet" href="../_static/css/theme.css" type="text/css" />
<script id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
<script src="../_static/jquery.js"></script> 
</head>
<body>
<nav id="navbar" class="sk-docs-navbar navbar navbar-expand-md navbar-light bg-light py-0">
  <div class="container-fluid sk-docs-container px-0">
      <a class="navbar-brand py-0" href="../index.html">
        <img
          class="sk-brand-img"
          src="../_static/scikit-learn-logo-small.png"
          alt="logo"/>
      </a>
    <button
      id="sk-navbar-toggler"
      class="navbar-toggler"
      type="button"
      data-toggle="collapse"
      data-target="#navbarSupportedContent"
      aria-controls="navbarSupportedContent"
      aria-expanded="false"
      aria-label="Toggle navigation"
    >
      <span class="navbar-toggler-icon"></span>
    </button>

    <div class="sk-navbar-collapse collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav mr-auto">
        <li class="nav-item">
          <a class="sk-nav-link nav-link" href="../install.html">Install</a>
        </li>
        <li class="nav-item">
          <a class="sk-nav-link nav-link" href="../user_guide.html">User Guide</a>
        </li>
        <li class="nav-item">
          <a class="sk-nav-link nav-link" href="classes.html">API</a>
        </li>
        <li class="nav-item">
          <a class="sk-nav-link nav-link" href="../auto_examples/index.html">Examples</a>
        </li>
        <li class="nav-item">
          <a class="sk-nav-link nav-link nav-more-item-mobile-items" href="../getting_started.html">Getting Started</a>
        </li>
        <li class="nav-item">
          <a class="sk-nav-link nav-link nav-more-item-mobile-items" href="../tutorial/index.html">Tutorial</a>
        </li>
        <li class="nav-item">
          <a class="sk-nav-link nav-link nav-more-item-mobile-items" href="../glossary.html">Glossary</a>
        </li>
        <li class="nav-item">
          <a class="sk-nav-link nav-link nav-more-item-mobile-items" href="../developers/index.html">Development</a>
        </li>
        <li class="nav-item">
          <a class="sk-nav-link nav-link nav-more-item-mobile-items" href="../faq.html">FAQ</a>
        </li>
        <li class="nav-item">
          <a class="sk-nav-link nav-link nav-more-item-mobile-items" href="../related_projects.html">Related packages</a>
        </li>
        <li class="nav-item">
          <a class="sk-nav-link nav-link nav-more-item-mobile-items" href="../roadmap.html">Roadmap</a>
        </li>
        <li class="nav-item">
          <a class="sk-nav-link nav-link nav-more-item-mobile-items" href="../about.html">About us</a>
        </li>
        <li class="nav-item">
          <a class="sk-nav-link nav-link nav-more-item-mobile-items" href="https://github.com/scikit-learn/scikit-learn">GitHub</a>
        </li>
        <li class="nav-item">
          <a class="sk-nav-link nav-link nav-more-item-mobile-items" href="https://scikit-learn.org/dev/versions.html">Other Versions</a>
        </li>
        <li class="nav-item dropdown nav-more-item-dropdown">
          <a class="sk-nav-link nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">More</a>
          <div class="dropdown-menu" aria-labelledby="navbarDropdown">
              <a class="sk-nav-dropdown-item dropdown-item" href="../getting_started.html">Getting Started</a>
              <a class="sk-nav-dropdown-item dropdown-item" href="../tutorial/index.html">Tutorial</a>
              <a class="sk-nav-dropdown-item dropdown-item" href="../glossary.html">Glossary</a>
              <a class="sk-nav-dropdown-item dropdown-item" href="../developers/index.html">Development</a>
              <a class="sk-nav-dropdown-item dropdown-item" href="../faq.html">FAQ</a>
              <a class="sk-nav-dropdown-item dropdown-item" href="../related_projects.html">Related packages</a>
              <a class="sk-nav-dropdown-item dropdown-item" href="../roadmap.html">Roadmap</a>
              <a class="sk-nav-dropdown-item dropdown-item" href="../about.html">About us</a>
              <a class="sk-nav-dropdown-item dropdown-item" href="https://github.com/scikit-learn/scikit-learn">GitHub</a>
              <a class="sk-nav-dropdown-item dropdown-item" href="https://scikit-learn.org/dev/versions.html">Other Versions</a>
          </div>
        </li>
      </ul>
      <div id="searchbox" role="search">
          <div class="searchformwrapper">
          <form class="search" action="../search.html" method="get">
            <input class="sk-search-text-input" type="text" name="q" aria-labelledby="searchlabel" />
            <input class="sk-search-text-btn" type="submit" value="Go" />
          </form>
          </div>
      </div>
    </div>
  </div>
</nav>
<div class="d-flex" id="sk-doc-wrapper">
    <input type="checkbox" name="sk-toggle-checkbox" id="sk-toggle-checkbox">
    <label id="sk-sidemenu-toggle" class="sk-btn-toggle-toc btn sk-btn-primary" for="sk-toggle-checkbox">Toggle Menu</label>
    <div id="sk-sidebar-wrapper" class="border-right">
      <div class="sk-sidebar-toc-wrapper">
        <div class="sk-sidebar-toc-logo">
          <a href="../index.html">
            <img
              class="sk-brand-img"
              src="../_static/scikit-learn-logo-small.png"
              alt="logo"/>
          </a>
        </div>
        <div class="btn-group w-100 mb-2" role="group" aria-label="rellinks">
            <a href="linear_model.html" role="button" class="btn sk-btn-rellink py-1" sk-rellink-tooltip="1.1. Linear Models">Prev</a><a href="../supervised_learning.html" role="button" class="btn sk-btn-rellink py-1" sk-rellink-tooltip="1. Supervised learning">Up</a>
            <a href="kernel_ridge.html" role="button" class="btn sk-btn-rellink py-1" sk-rellink-tooltip="1.3. Kernel ridge regression">Next</a>
        </div>
        <div class="alert alert-danger p-1 mb-2" role="alert">
          <p class="text-center mb-0">
          <strong>scikit-learn 0.22</strong><br/>
          <a href="http://scikit-learn.org/dev/versions.html">Other versions</a>
          </p>
        </div>
        <div class="alert alert-warning p-1 mb-2" role="alert">
          <p class="text-center mb-0">
            Please <a class="font-weight-bold" href="../about.html#citing-scikit-learn"><string>cite us</string></a> if you use the software.
          </p>
        </div>
          <div class="sk-sidebar-toc">
            <ul>
<li><a class="reference internal" href="#">1.2. Linear and Quadratic Discriminant Analysis</a><ul>
<li><a class="reference internal" href="#dimensionality-reduction-using-linear-discriminant-analysis">1.2.1. Dimensionality reduction using Linear Discriminant Analysis</a></li>
<li><a class="reference internal" href="#mathematical-formulation-of-the-lda-and-qda-classifiers">1.2.2. Mathematical formulation of the LDA and QDA classifiers</a></li>
<li><a class="reference internal" href="#mathematical-formulation-of-lda-dimensionality-reduction">1.2.3. Mathematical formulation of LDA dimensionality reduction</a></li>
<li><a class="reference internal" href="#shrinkage">1.2.4. Shrinkage</a></li>
<li><a class="reference internal" href="#estimation-algorithms">1.2.5. Estimation algorithms</a></li>
</ul>
</li>
</ul>

          </div>
      </div>
    </div>
    <div id="sk-page-content-wrapper">
      <div class="sk-page-content container-fluid body px-md-3" role="main">
        
  <div class="section" id="linear-and-quadratic-discriminant-analysis">
<span id="lda-qda"></span><h1>1.2. Linear and Quadratic Discriminant Analysis<a class="headerlink" href="#linear-and-quadratic-discriminant-analysis" title="Permalink to this headline">¶</a></h1>
<p>Linear Discriminant Analysis
(<a class="reference internal" href="generated/sklearn.discriminant_analysis.LinearDiscriminantAnalysis.html#sklearn.discriminant_analysis.LinearDiscriminantAnalysis" title="sklearn.discriminant_analysis.LinearDiscriminantAnalysis"><code class="xref py py-class docutils literal notranslate"><span class="pre">discriminant_analysis.LinearDiscriminantAnalysis</span></code></a>) and Quadratic
Discriminant Analysis
(<a class="reference internal" href="generated/sklearn.discriminant_analysis.QuadraticDiscriminantAnalysis.html#sklearn.discriminant_analysis.QuadraticDiscriminantAnalysis" title="sklearn.discriminant_analysis.QuadraticDiscriminantAnalysis"><code class="xref py py-class docutils literal notranslate"><span class="pre">discriminant_analysis.QuadraticDiscriminantAnalysis</span></code></a>) are two classic
classifiers, with, as their names suggest, a linear and a quadratic decision
surface, respectively.</p>
<p>These classifiers are attractive because they have closed-form solutions that
can be easily computed, are inherently multiclass, have proven to work well in
practice, and have no hyperparameters to tune.</p>
<p class="centered">
<strong><a class="reference external" href="../auto_examples/classification/plot_lda_qda.html"><img alt="ldaqda" src="modules/../auto_examples/classification/images/sphx_glr_plot_lda_qda_001.png" /></a></strong></p><p>The plot shows decision boundaries for Linear Discriminant Analysis and
Quadratic Discriminant Analysis. The bottom row demonstrates that Linear
Discriminant Analysis can only learn linear boundaries, while Quadratic
Discriminant Analysis can learn quadratic boundaries and is therefore more
flexible.</p>
<div class="topic">
<p class="topic-title">Examples:</p>
<p><a class="reference internal" href="../auto_examples/classification/plot_lda_qda.html#sphx-glr-auto-examples-classification-plot-lda-qda-py"><span class="std std-ref">Linear and Quadratic Discriminant Analysis with covariance ellipsoid</span></a>: Comparison of LDA and QDA
on synthetic data.</p>
</div>
<div class="section" id="dimensionality-reduction-using-linear-discriminant-analysis">
<h2>1.2.1. Dimensionality reduction using Linear Discriminant Analysis<a class="headerlink" href="#dimensionality-reduction-using-linear-discriminant-analysis" title="Permalink to this headline">¶</a></h2>
<p><a class="reference internal" href="generated/sklearn.discriminant_analysis.LinearDiscriminantAnalysis.html#sklearn.discriminant_analysis.LinearDiscriminantAnalysis" title="sklearn.discriminant_analysis.LinearDiscriminantAnalysis"><code class="xref py py-class docutils literal notranslate"><span class="pre">discriminant_analysis.LinearDiscriminantAnalysis</span></code></a> can be used to
perform supervised dimensionality reduction, by projecting the input data to a
linear subspace consisting of the directions which maximize the separation
between classes (in a precise sense discussed in the mathematics section
below). The dimension of the output is necessarily less than the number of
classes, so this is, in general, a rather strong dimensionality reduction, and
only makes sense in a multiclass setting.</p>
<p>This is implemented in
<a class="reference internal" href="generated/sklearn.discriminant_analysis.LinearDiscriminantAnalysis.html#sklearn.discriminant_analysis.LinearDiscriminantAnalysis.transform" title="sklearn.discriminant_analysis.LinearDiscriminantAnalysis.transform"><code class="xref py py-func docutils literal notranslate"><span class="pre">discriminant_analysis.LinearDiscriminantAnalysis.transform</span></code></a>. The desired
dimensionality can be set using the <code class="docutils literal notranslate"><span class="pre">n_components</span></code> constructor parameter.
This parameter has no influence on
<a class="reference internal" href="generated/sklearn.discriminant_analysis.LinearDiscriminantAnalysis.html#sklearn.discriminant_analysis.LinearDiscriminantAnalysis.fit" title="sklearn.discriminant_analysis.LinearDiscriminantAnalysis.fit"><code class="xref py py-func docutils literal notranslate"><span class="pre">discriminant_analysis.LinearDiscriminantAnalysis.fit</span></code></a> or
<a class="reference internal" href="generated/sklearn.discriminant_analysis.LinearDiscriminantAnalysis.html#sklearn.discriminant_analysis.LinearDiscriminantAnalysis.predict" title="sklearn.discriminant_analysis.LinearDiscriminantAnalysis.predict"><code class="xref py py-func docutils literal notranslate"><span class="pre">discriminant_analysis.LinearDiscriminantAnalysis.predict</span></code></a>.</p>
<div class="topic">
<p class="topic-title">Examples:</p>
<p><a class="reference internal" href="../auto_examples/decomposition/plot_pca_vs_lda.html#sphx-glr-auto-examples-decomposition-plot-pca-vs-lda-py"><span class="std std-ref">Comparison of LDA and PCA 2D projection of Iris dataset</span></a>: Comparison of LDA and PCA
for dimensionality reduction of the Iris dataset</p>
</div>
</div>
<div class="section" id="mathematical-formulation-of-the-lda-and-qda-classifiers">
<h2>1.2.2. Mathematical formulation of the LDA and QDA classifiers<a class="headerlink" href="#mathematical-formulation-of-the-lda-and-qda-classifiers" title="Permalink to this headline">¶</a></h2>
<p>Both LDA and QDA can be derived from simple probabilistic models which model
the class conditional distribution of the data <span class="math notranslate nohighlight">\(P(X|y=k)\)</span> for each class
<span class="math notranslate nohighlight">\(k\)</span>. Predictions can then be obtained by using Bayes’ rule:</p>
<div class="math notranslate nohighlight">
\[P(y=k | X) = \frac{P(X | y=k) P(y=k)}{P(X)} = \frac{P(X | y=k) P(y = k)}{ \sum_{l} P(X | y=l) \cdot P(y=l)}\]</div>
<p>and we select the class <span class="math notranslate nohighlight">\(k\)</span> which maximizes this conditional probability.</p>
<p>More specifically, for linear and quadratic discriminant analysis,
<span class="math notranslate nohighlight">\(P(X|y)\)</span> is modeled as a multivariate Gaussian distribution with
density:</p>
<div class="math notranslate nohighlight">
\[P(X | y=k) = \frac{1}{(2\pi)^{d/2} |\Sigma_k|^{1/2}}\exp\left(-\frac{1}{2} (X-\mu_k)^t \Sigma_k^{-1} (X-\mu_k)\right)\]</div>
<p>where <span class="math notranslate nohighlight">\(d\)</span> is the number of features.</p>
<p>To use this model as a classifier, we just need to estimate from the training
data the class priors <span class="math notranslate nohighlight">\(P(y=k)\)</span> (by the proportion of instances of class
<span class="math notranslate nohighlight">\(k\)</span>), the class means <span class="math notranslate nohighlight">\(\mu_k\)</span> (by the empirical sample class means)
and the covariance matrices (either by the empirical sample class covariance
matrices, or by a regularized estimator: see the section on shrinkage below).</p>
<p>In the case of LDA, the Gaussians for each class are assumed to share the same
covariance matrix: <span class="math notranslate nohighlight">\(\Sigma_k = \Sigma\)</span> for all <span class="math notranslate nohighlight">\(k\)</span>. This leads to
linear decision surfaces, which can be seen by comparing the
log-probability ratios <span class="math notranslate nohighlight">\(\log[P(y=k | X) / P(y=l | X)]\)</span>:</p>
<div class="math notranslate nohighlight">
\[ \begin{align}\begin{aligned}\log\left(\frac{P(y=k|X)}{P(y=l|X)}\right)=
\log\left(\frac{P(X|y=k)P(y=k)}{P(X|y=l)P(y=l)}\right)=0 \Leftrightarrow\\(\mu_k-\mu_l)^t\Sigma^{-1} X =
\frac{1}{2} (\mu_k^t \Sigma^{-1} \mu_k - \mu_l^t \Sigma^{-1} \mu_l)
- \log\frac{P(y=k)}{P(y=l)}\end{aligned}\end{align} \]</div>
<p>In the case of QDA, there are no assumptions on the covariance matrices
<span class="math notranslate nohighlight">\(\Sigma_k\)</span> of the Gaussians, leading to quadratic decision surfaces. See
<a class="footnote-reference brackets" href="#id4" id="id1">3</a> for more details.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p><strong>Relation with Gaussian Naive Bayes</strong></p>
<p>If in the QDA model one assumes that the covariance matrices are diagonal,
then the inputs are assumed to be conditionally independent in each class,
and the resulting classifier is equivalent to the Gaussian Naive Bayes
classifier <a class="reference internal" href="generated/sklearn.naive_bayes.GaussianNB.html#sklearn.naive_bayes.GaussianNB" title="sklearn.naive_bayes.GaussianNB"><code class="xref py py-class docutils literal notranslate"><span class="pre">naive_bayes.GaussianNB</span></code></a>.</p>
</div>
</div>
<div class="section" id="mathematical-formulation-of-lda-dimensionality-reduction">
<h2>1.2.3. Mathematical formulation of LDA dimensionality reduction<a class="headerlink" href="#mathematical-formulation-of-lda-dimensionality-reduction" title="Permalink to this headline">¶</a></h2>
<p>To understand the use of LDA in dimensionality reduction, it is useful to start
with a geometric reformulation of the LDA classification rule explained above.
We write <span class="math notranslate nohighlight">\(K\)</span> for the total number of target classes. Since in LDA we
assume that all classes have the same estimated covariance <span class="math notranslate nohighlight">\(\Sigma\)</span>, we
can rescale the data so that this covariance is the identity:</p>
<div class="math notranslate nohighlight">
\[X^* = D^{-1/2}U^t X\text{ with }\Sigma = UDU^t\]</div>
<p>Then one can show that to classify a data point after scaling is equivalent to
finding the estimated class mean <span class="math notranslate nohighlight">\(\mu^*_k\)</span> which is closest to the data
point in the Euclidean distance. But this can be done just as well after
projecting on the <span class="math notranslate nohighlight">\(K-1\)</span> affine subspace <span class="math notranslate nohighlight">\(H_K\)</span> generated by all the
<span class="math notranslate nohighlight">\(\mu^*_k\)</span> for all classes. This shows that, implicit in the LDA
classifier, there is a dimensionality reduction by linear projection onto a
<span class="math notranslate nohighlight">\(K-1\)</span> dimensional space.</p>
<p>We can reduce the dimension even more, to a chosen <span class="math notranslate nohighlight">\(L\)</span>, by projecting
onto the linear subspace <span class="math notranslate nohighlight">\(H_L\)</span> which maximizes the variance of the
<span class="math notranslate nohighlight">\(\mu^*_k\)</span> after projection (in effect, we are doing a form of PCA for the
transformed class means <span class="math notranslate nohighlight">\(\mu^*_k\)</span>). This <span class="math notranslate nohighlight">\(L\)</span> corresponds to the
<code class="docutils literal notranslate"><span class="pre">n_components</span></code> parameter used in the
<a class="reference internal" href="generated/sklearn.discriminant_analysis.LinearDiscriminantAnalysis.html#sklearn.discriminant_analysis.LinearDiscriminantAnalysis.transform" title="sklearn.discriminant_analysis.LinearDiscriminantAnalysis.transform"><code class="xref py py-func docutils literal notranslate"><span class="pre">discriminant_analysis.LinearDiscriminantAnalysis.transform</span></code></a> method. See
<a class="footnote-reference brackets" href="#id4" id="id2">3</a> for more details.</p>
</div>
<div class="section" id="shrinkage">
<h2>1.2.4. Shrinkage<a class="headerlink" href="#shrinkage" title="Permalink to this headline">¶</a></h2>
<p>Shrinkage is a tool to improve estimation of covariance matrices in situations
where the number of training samples is small compared to the number of
features. In this scenario, the empirical sample covariance is a poor
estimator. Shrinkage LDA can be used by setting the <code class="docutils literal notranslate"><span class="pre">shrinkage</span></code> parameter of
the <a class="reference internal" href="generated/sklearn.discriminant_analysis.LinearDiscriminantAnalysis.html#sklearn.discriminant_analysis.LinearDiscriminantAnalysis" title="sklearn.discriminant_analysis.LinearDiscriminantAnalysis"><code class="xref py py-class docutils literal notranslate"><span class="pre">discriminant_analysis.LinearDiscriminantAnalysis</span></code></a> class to ‘auto’.
This automatically determines the optimal shrinkage parameter in an analytic
way following the lemma introduced by Ledoit and Wolf <a class="footnote-reference brackets" href="#id5" id="id3">4</a>. Note that
currently shrinkage only works when setting the <code class="docutils literal notranslate"><span class="pre">solver</span></code> parameter to ‘lsqr’
or ‘eigen’.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">shrinkage</span></code> parameter can also be manually set between 0 and 1. In
particular, a value of 0 corresponds to no shrinkage (which means the empirical
covariance matrix will be used) and a value of 1 corresponds to complete
shrinkage (which means that the diagonal matrix of variances will be used as
an estimate for the covariance matrix). Setting this parameter to a value
between these two extrema will estimate a shrunk version of the covariance
matrix.</p>
<p class="centered">
<strong><a class="reference external" href="../auto_examples/classification/plot_lda.html"><img alt="shrinkage" src="modules/../auto_examples/classification/images/sphx_glr_plot_lda_001.png" /></a></strong></p></div>
<div class="section" id="estimation-algorithms">
<h2>1.2.5. Estimation algorithms<a class="headerlink" href="#estimation-algorithms" title="Permalink to this headline">¶</a></h2>
<p>The default solver is ‘svd’. It can perform both classification and transform,
and it does not rely on the calculation of the covariance matrix. This can be
an advantage in situations where the number of features is large. However, the
‘svd’ solver cannot be used with shrinkage.</p>
<p>The ‘lsqr’ solver is an efficient algorithm that only works for classification.
It supports shrinkage.</p>
<p>The ‘eigen’ solver is based on the optimization of the between class scatter to
within class scatter ratio. It can be used for both classification and
transform, and it supports shrinkage. However, the ‘eigen’ solver needs to
compute the covariance matrix, so it might not be suitable for situations with
a high number of features.</p>
<div class="topic">
<p class="topic-title">Examples:</p>
<p><a class="reference internal" href="../auto_examples/classification/plot_lda.html#sphx-glr-auto-examples-classification-plot-lda-py"><span class="std std-ref">Normal and Shrinkage Linear Discriminant Analysis for classification</span></a>: Comparison of LDA classifiers
with and without shrinkage.</p>
</div>
<div class="topic">
<p class="topic-title">References:</p>
<dl class="footnote brackets">
<dt class="label" id="id4"><span class="brackets">3</span><span class="fn-backref">(<a href="#id1">1</a>,<a href="#id2">2</a>)</span></dt>
<dd><p>“The Elements of Statistical Learning”, Hastie T., Tibshirani R.,
Friedman J., Section 4.3, p.106-119, 2008.</p>
</dd>
<dt class="label" id="id5"><span class="brackets"><a class="fn-backref" href="#id3">4</a></span></dt>
<dd><p>Ledoit O, Wolf M. Honey, I Shrunk the Sample Covariance Matrix.
The Journal of Portfolio Management 30(4), 110-119, 2004.</p>
</dd>
</dl>
</div>
</div>
</div>


      </div>
    <div class="container">
      <footer class="sk-content-footer">
            &copy; 2007 - 2019, scikit-learn developers (BSD License).
          <a href="../_sources/modules/lda_qda.rst.txt" rel="nofollow">Show this page source</a>
      </footer>
    </div>
  </div>
</div>
<script src="../_static/js/vendor/bootstrap.min.js"></script>

<script>
    window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
    ga('create', 'UA-22606712-2', 'auto');
    ga('set', 'anonymizeIp', true);
    ga('send', 'pageview');
</script>
<script async src='https://www.google-analytics.com/analytics.js'></script>


<script>
$(document).ready(function() {
    /* Add a [>>>] button on the top-right corner of code samples to hide
     * the >>> and ... prompts and the output and thus make the code
     * copyable. */
    var div = $('.highlight-python .highlight,' +
                '.highlight-python3 .highlight,' +
                '.highlight-pycon .highlight,' +
		'.highlight-default .highlight')
    var pre = div.find('pre');

    // get the styles from the current theme
    pre.parent().parent().css('position', 'relative');
    var hide_text = 'Hide prompts and outputs';
    var show_text = 'Show prompts and outputs';

    // create and add the button to all the code blocks that contain >>>
    div.each(function(index) {
        var jthis = $(this);
        if (jthis.find('.gp').length > 0) {
            var button = $('<span class="copybutton">&gt;&gt;&gt;</span>');
            button.attr('title', hide_text);
            button.data('hidden', 'false');
            jthis.prepend(button);
        }
        // tracebacks (.gt) contain bare text elements that need to be
        // wrapped in a span to work with .nextUntil() (see later)
        jthis.find('pre:has(.gt)').contents().filter(function() {
            return ((this.nodeType == 3) && (this.data.trim().length > 0));
        }).wrap('<span>');
    });

    // define the behavior of the button when it's clicked
    $('.copybutton').click(function(e){
        e.preventDefault();
        var button = $(this);
        if (button.data('hidden') === 'false') {
            // hide the code output
            button.parent().find('.go, .gp, .gt').hide();
            button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'hidden');
            button.css('text-decoration', 'line-through');
            button.attr('title', show_text);
            button.data('hidden', 'true');
        } else {
            // show the code output
            button.parent().find('.go, .gp, .gt').show();
            button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'visible');
            button.css('text-decoration', 'none');
            button.attr('title', hide_text);
            button.data('hidden', 'false');
        }
    });

	/*** Add permalink buttons next to glossary terms ***/
	$('dl.glossary > dt[id]').append(function() {
		return ('<a class="headerlink" href="#' +
			    this.getAttribute('id') +
			    '" title="Permalink to this term">¶</a>');
	});
  /*** Hide navbar when scrolling down ***/
  // Returns true when headerlink target matches hash in url
  (function() {
    hashTargetOnTop = function() {
        var hash = window.location.hash;
        if ( hash.length < 2 ) { return false; }

        var target = document.getElementById( hash.slice(1) );
        if ( target === null ) { return false; }

        var top = target.getBoundingClientRect().top;
        return (top < 2) && (top > -2);
    };

    // Hide navbar on load if hash target is on top
    var navBar = document.getElementById("navbar");
    var navBarToggler = document.getElementById("sk-navbar-toggler");
    var navBarHeightHidden = "-" + navBar.getBoundingClientRect().height + "px";
    var $window = $(window);

    hideNavBar = function() {
        navBar.style.top = navBarHeightHidden;
    };

    showNavBar = function() {
        navBar.style.top = "0";
    }

    if (hashTargetOnTop()) {
        hideNavBar()
    }

    var prevScrollpos = window.pageYOffset;
    hideOnScroll = function(lastScrollTop) {
        if (($window.width() < 768) && (navBarToggler.getAttribute("aria-expanded") === 'true')) {
            return;
        }
        if (lastScrollTop > 2 && (prevScrollpos <= lastScrollTop) || hashTargetOnTop()){
            hideNavBar()
        } else {
            showNavBar()
        }
        prevScrollpos = lastScrollTop;
    };

    /*** high preformance scroll event listener***/
    var raf = window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        window.oRequestAnimationFrame;
    var lastScrollTop = $window.scrollTop();

    if (raf) {
        loop();
    }

    function loop() {
        var scrollTop = $window.scrollTop();
        if (lastScrollTop === scrollTop) {
            raf(loop);
            return;
        } else {
            lastScrollTop = scrollTop;
            hideOnScroll(lastScrollTop);
            raf(loop);
        }
    }
  })();
});

</script>
    
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
    
</body>
</html>